// SPDX-License-Identifier: GPL-2.0-only
/*
 * NOTE: This example is works on x86 and powerpc.
 * Here's a sample kernel module showing the use of kprobes to dump a
 * stack trace and selected registers when _do_fork() is called.
 *
 * For more information on theory of operation of kprobes, see
 * Documentation/kprobes.txt
 *
 * You will see the trace data in /var/log/messages and on the console
 * whenever _do_fork() is invoked to create a new process.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#include "kprobe_prehook.h" 
#include "data_aware.h" 

static char symbol[NAME_MAX] = "syscall_return_slowpath";
char symbol_new[NAME_MAX] = "syscall_return_slowpath";
unsigned int sysctl_kprobe_unregister;

/* For each probe you need to allocate a kprobe structure */
static struct kprobe kp = {
	.symbol_name	= symbol,
};

static struct kprobe kp_new = {
	.symbol_name	= symbol_new,
};
/*
void stat_do_syscall_64(unsigned long nr, struct pt_regs *regs)
{
	pr_info("sys:%ld, %ld", nr, regs->orig_ax);
	do_syscall_64(nr, regs);
}
*/

/* kprobe pre_handler: called just before the probed instruction is executed */
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
{
	//stat_stat_syscall_enter();
	/* A dump_stack() here will give a stack backtrace */
	return 0;
}

/* kprobe post_handler: called after the probed instruction is executed */
static void handler_post(struct kprobe *p, struct pt_regs *regs,
				unsigned long flags)
{
}

/*
 * fault_handler: this is called if an exception is generated for any
 * instruction within the pre- or post-handler, or when Kprobes
 * single-steps the probed instruction.
 */
static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr)
{
	pr_info("fault_handler: p->addr = 0x%p, trap #%dn", p->addr, trapnr);
	/* Return 0 because we don't handle the fault. */
	return 0;
}

static int register_new_func(char *name)
{
	int ret;

	memcpy(symbol_new, name, sizeof(name));
	kp_new.pre_handler = handler_pre;
	kp_new.post_handler = handler_post;
	ret = register_kprobe(&kp_new);
	if (ret < 0) {
		pr_err("register_kprobe failed, returned %d, %s\n", ret, name);
		return ret;
	}
	return 0;
}

static void ungister_new_func(void)
{
	unregister_kprobe(&kp_new);
}

int sysctl_kprobe_enable_handler(struct ctl_table *table, int write,
		void __user *buffer, size_t *lenp, loff_t *ppos)
{
	int ret = 0;

	if (write && !capable(CAP_SYS_ADMIN))
		return -EPERM;

	if (sysctl_module_disable)
		return -EPERM;

	ret = proc_dostring(table, write, buffer, lenp, ppos);
	register_new_func(symbol_new);

	return ret;
}

int sysctl_kprobe_disable_handler(struct ctl_table *table, int write,
		void __user *buffer, size_t *lenp, loff_t *ppos)
{
	int ret;

	if (write && !capable(CAP_SYS_ADMIN))
		return -EPERM;

	if (sysctl_module_disable)
		return -EPERM;

	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);

	ungister_new_func();

	return ret;
}
int __kprobe_init(void)
{
	int ret;
	kp.pre_handler = handler_pre;
	kp.post_handler = handler_post;
#ifndef TK5
	kp.fault_handler = handler_fault;
#endif

	ret = register_kprobe(&kp);
	if (ret < 0) {
		pr_err("register_kprobe failed, returned %d\n", ret);
		return ret;
	}
	pr_info("Planted kprobe at %p\n", kp.addr);
	return 0;
}
int kprobe_init(void)
{
	//return __kprobe_init();
	return 0;
}

void __kprobe_exit(void)
{
	unregister_kprobe(&kp);
	pr_info("kprobe at %p unregistered\n", kp.addr);

	return;
}

void kprobe_exit(void)
{
	//__kprobe_exit();
	return;
}

